package com.hanyi.web.controller.system;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.List;
import java.util.Arrays;
import java.util.Set;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.NetUtil;
import com.hanyi.common.utils.StringUtils;
import com.hanyi.system.domain.SysLocalTask;
import com.hanyi.system.domain.SysRealtimeTask;
import com.hanyi.web.controller.task.ScheduleTask;
import lombok.RequiredArgsConstructor;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import com.hanyi.common.annotation.RepeatSubmit;
import com.hanyi.common.annotation.Log;
import com.hanyi.common.core.controller.BaseController;
import com.hanyi.common.core.domain.PageQuery;
import com.hanyi.common.core.domain.R;
import com.hanyi.common.core.validate.AddGroup;
import com.hanyi.common.core.validate.EditGroup;
import com.hanyi.common.core.validate.QueryGroup;
import com.hanyi.common.enums.BusinessType;
import com.hanyi.common.utils.poi.ExcelUtil;
import com.hanyi.system.domain.vo.SysLocalTaskVo;
import com.hanyi.system.domain.bo.SysLocalTaskBo;
import com.hanyi.system.service.ISysLocalTaskService;
import com.hanyi.common.core.page.TableDataInfo;

/**
 * 本地任务
 *
 * @author grantfee
 * @date 2023-11-06
 */

@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/localTask")
public class SysLocalTaskController extends BaseController {

    private final ISysLocalTaskService iSysLocalTaskService;

    private final ScheduleTask scheduleTask;

    /**
     * 查询本地任务列表
     */
    @SaCheckPermission("system:localTask:list")
    @GetMapping("/list")
    public TableDataInfo<SysLocalTaskVo> list(SysLocalTaskBo bo, PageQuery pageQuery) {
        return iSysLocalTaskService.queryPageList(bo, pageQuery);
    }

    /**
     * 导出本地任务列表
     */
    @SaCheckPermission("system:localTask:export")
    @Log(title = "本地任务", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(SysLocalTaskBo bo, HttpServletResponse response) {
        List<SysLocalTaskVo> list = iSysLocalTaskService.queryList(bo);
        ExcelUtil.exportExcel(list, "本地任务", SysLocalTaskVo.class, response);
    }

    /**
     * 获取本地任务详细信息
     *
     * @param taskId 主键
     */
    @SaCheckPermission("system:localTask:query")
    @GetMapping("/{taskId}")
    public R<SysLocalTaskVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable Long taskId) {
        return R.ok(iSysLocalTaskService.queryById(taskId));
    }

    /**
     * 新增本地任务
     */
    @SaCheckPermission("system:localTask:add")
    @Log(title = "本地任务", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysLocalTaskBo bo) {
        String videoPath = "";
        if(bo.getIsLocalFile().equals("1")){//本地文件
            videoPath = bo.getVideoPath()+"/"+bo.getVideoName();
        }else if(bo.getIsLocalFile().equals("2")){
            File mediaFile = new File("/media");
            String[] usbPaths = mediaFile.list();
            if(usbPaths!=null){
                for(String usbPath :usbPaths){
                    log.info("usb path:"+usbPath);
                    String targetVideo = "/media/"+usbPath+bo.getVideoPath()+"/"+bo.getVideoName();
                    if(FileUtil.exist(targetVideo)){
                        videoPath = targetVideo;
                        break;
                    }
                }
            }

        }
        if(StringUtils.isNotEmpty(videoPath)){
            String externalIp = "127.0.0.1";
            NetworkInterface networkInterface =  NetUtil.getNetworkInterface("eth0");
            List<InterfaceAddress> list= networkInterface.getInterfaceAddresses();
            for(InterfaceAddress interfaceAddress :list){
                String host  = interfaceAddress.getAddress().getHostAddress();
                log.info("local host ip:"+host);
                if(host.contains("eth0")){
                    continue;
                }
                externalIp = host;
            }
            bo.setRtspUrl("http://"+ externalIp+":9001/"+videoPath);
            return toAjax(iSysLocalTaskService.insertByBo(bo) ? 1 : 0);
        }

        return toAjax(false);

    }

    /**
     * 修改本地任务
     */
    @SaCheckPermission("system:localTask:edit")
    @Log(title = "本地任务", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysLocalTaskBo bo) {
        return toAjax(iSysLocalTaskService.updateByBo(bo) ? 1 : 0);
    }

    /**
     * 删除本地任务
     *
     * @param taskIds 主键串
     */
    @SaCheckPermission("system:localTask:remove")
    @Log(title = "本地任务", businessType = BusinessType.DELETE)
    @DeleteMapping("/{taskIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] taskIds) {
        return toAjax(iSysLocalTaskService.deleteWithValidByIds(Arrays.asList(taskIds), true) ? 1 : 0);
    }

    /**
     * 启动实时任务
     *
     * @param taskId 主键
     */
    @SaCheckPermission("system:realtimeTask:query")
    @GetMapping("/start/{taskId}")
    public R<Void> startTask(@NotNull(message = "主键不能为空")
                             @PathVariable Long taskId) {

        //发送消息到边缘盒子启动任务
        SysLocalTask task = iSysLocalTaskService.queryByTaskId(taskId);
        log.info("prepare to start local task:"+taskId+",is local file:"+task.getIsLocalFile());
        String videoPath = "";
        if(task.getIsLocalFile().equals("1")){//本地文件
            videoPath = task.getVideoPath()+task.getVideoName();
        }else if(task.getIsLocalFile().equals("2")){//USB外挂文件
            if(FileUtil.exist("/media")){
                log.error("media file exists");
                File mediaFile = new File("/media");
                String[] usbPaths = mediaFile.list();
                if(usbPaths!=null){
                    for(String usbPath :usbPaths){
                        log.info("usb path:"+usbPath);
                        String targetVideo = "/media/"+usbPath+task.getVideoPath()+task.getVideoName();
                        if(FileUtil.exist(targetVideo)){
                            videoPath = targetVideo;
                            break;
                        }
                    }
                }
            }else{
                log.error("media file not exists");
            }
        }

        if(StringUtils.isNotEmpty(videoPath)){
            log.info("video path:"+videoPath);
            boolean ok = scheduleTask.startLocalTask(task.getTaskId(),videoPath);
            if(ok){
                task.setStatus("1");
                return R.ok();
            }

            iSysLocalTaskService.updateByEntity(task);
        }

        return R.fail("启动任务失败!请检查参数后重试");
    }

    @Async
    protected void startTaskAsync(SysLocalTask task){
        scheduleTask.startLocalTask(task.getTaskId(),task.getVideoPath());
    }
    /**
     * 停止实时任务
     *
     * @param taskId 主键
     */
    @SaCheckPermission("system:realtimeTask:query")
    @GetMapping("/stop/{taskId}")
    public R<Void> stopTask(@NotNull(message = "主键不能为空")
                            @PathVariable Long taskId) {
        log.info("prepare to stop local task:"+taskId);
        //发送消息到边缘盒子停止任务
        SysLocalTask task = iSysLocalTaskService.queryByTaskId(taskId);
        task.setStatus("0");
        iSysLocalTaskService.updateByEntity(task);

        stopTaskAsync(taskId);
        return R.ok();
    }

    @Async
    protected void stopTaskAsync(long taskId){
        scheduleTask.stopTask(taskId);
    }


    @GetMapping("/play/{taskId}")
    public R playVideo(@NotNull(message = "主键不能为空")
                           @PathVariable Long taskId){

        SysLocalTask task = iSysLocalTaskService.queryByTaskId(taskId);
        String videoPath = "";
        if(task.getIsLocalFile()=="1"){//本地文件
            videoPath = task.getVideoPath()+task.getVideoName();
        }else if(task.getIsLocalFile()=="2"){
            List<String> usbPaths =  FileUtil.listFileNames("/media");
            for(String usbPath :usbPaths){
                log.info("usb path:"+usbPath);
                String targetVideo = "/media/"+usbPath+"/"+task.getVideoPath()+task.getVideoName();
                if(FileUtil.exist(targetVideo)){
                    videoPath = targetVideo;
                    break;
                }
            }
        }
        if(StringUtils.isNotEmpty(videoPath)){
            pushVideoAsRTSP(taskId,videoPath);
        }else{
            return R.fail("failed to find video file");
        }

        return R.ok("play video ok");
    }
    /**
     *
     * java调用ffmpeg推流
     */
    private Process process;
    @Async
    protected int pushVideoAsRTSP(long taskId,String videoPath){
        int flag = 0;
        // ffmpeg位置，最好写在配置文件中
        String rtspStream = "rtsp://192.168.31.100:8554/"+taskId+"/stream";
        try {
            // 视频切换时，先销毁进程，全局变量Process process，方便进程销毁重启，即切换推流视频
            if(process != null){
                process.destroy();
                log.info(">>>>>>>>>>推流视频切换,关闭当前<<<<<<<<<<");
            }
            // cmd命令拼接，注意命令中存在空格
            String command = ""; // ffmpeg位置
            command += "/usr/bin/ffmpeg -re"; // ffmpeg开头，-re代表按照帧率发送，在推流时必须有
            command += " -i " + videoPath; // 指定要推送的视频
            command +="-vcodec copy -acodec copy";
            command += " -f rtsp -rtsp_transport tcp " + rtspStream; // 指定推送服务器，-f：指定格式
            log.info("ffmpeg推流 " + command);

            // 运行cmd命令，获取其进程
            process = Runtime.getRuntime().exec(command);
            // 输出ffmpeg推流日志
            BufferedReader br= new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line = "";
            while ((line = br.readLine()) != null) {
                log.info("视频推流信息[" + line + "]");
            }
            flag = process.waitFor();
        }catch (Exception e){
            e.printStackTrace();
        }
        return flag;
    }
}
